/**
 * author:Administrator
 * date:2015/7/27
 * desc:创立 elastic search 链接，建立 index/type，在es的curd之前的准备工作
 */

'use strict';
var debugLog = require('../../function.js').debugLog;

let elasticsearch = require('elasticsearch');
var config = require('../../../config');
let EventEmitter = require('events').EventEmitter;
let util = require('util');
let __index = config.elasticSearch.index;
console.log('ES:  ' + config.elasticSearch.host);
let client = new elasticsearch.Client({
  host: config.elasticSearch.host,
  //log: 'trace', // 打印日志
  requestTimeout: Infinity, // Tested
  keepAlive: true // Tested
});

function EsClient() {
  this.host = config.elasticSearch.host;
  this.index = __index;
  this.client = client;
  this.indexReady = false;
  this.readyType = [];
  this.enroll = {}; //{type:xxx,mapping:xxx}

  console.log('--------------es config, host=%s,index=%s', this.host, this.index);
}

util.inherits(EsClient, EventEmitter);


//ping the server
client.ping({
  requestTimeout: 20000,
  hello: "elasticsearch"
}, function (error) {
  if (error) {
    console.error('elastic not reachable!');
    // throw new Error('elasticsearch cluster is down!');
  } else {
    console.log('--------------ping es server, all is well');
    esClient.createIndexIfNecessary(__index);
  }
});

EsClient.prototype.dropTypeReady = function (type) {
  let self = this;
  let index = self.readyType.indexOf(type);
  if (index >= 0) {
    self.readyType.splice(index, 1);
  }
};
/**
 * check index , if not exists, create it
 * @param index
 */
EsClient.prototype.createIndexIfNecessary = function () {
  console.log('--------------createIndexIfNecessary');
  if (this.indexReady) {
    return;
  }

  let self = this;
  client.indices.exists({
    index: __index
  }).then(function fullfil(body) { // create index if necessary
    if (!body) {
      self.createIndex(__index);
    } else {
      self.afterIndexReady();
    }
  });
};

EsClient.prototype.createIndex = function () {
  console.log('--------------createIndex');
  let self = this;
  return client.indices.create({
    index: __index
  }).then(function fullfil(body) { // create index if necessary
    console.log('create index succ', body);
    if (body.acknowledged) {
      self.afterIndexReady();
    }
    try {
      self.putSettings();
    } catch (err) {
      console.log('put setting error', err);
    }
  }, function reject(error) {
    console.log('reject', error);
    self.afterIndexReady();
  });
};


// TODO cannot success
var request = require('request');
EsClient.prototype.putSettings = function () {
  return;
  console.log('--------------putSettings');
  var self = this;
  // close, put, open
  var url = 'http://' + self.host + '/' + self.index;
  var closeUrl = url + '/_close';
  var openUrl = url + '/_open';
  var settingUrl = url + '/_settings';
  console.log(closeUrl);
  console.log(openUrl);
  console.log(settingUrl);

  return new Promise(function (fulfil, reject) {
    setTimeout(function () {
      request({
        url: closeUrl,
        method: 'POST'
      }, function (err, res, body) {
        if (err) reject(err);
        else fulfil(body);
      });
    }, 1000);
  })

    .then(function (body) {
      console.log('close index success', body);
      var setting = {
        "settings": {
          "similarity": {
            "custom": {
              "type": "BM25",
              "k1": "2.0",
              "b": "1.5"
            }
          }
        }
      };
      //return new Promise(function (fulfil, reject) {
      //  request({
      //    url: settingUrl,
      //    method: 'PUT',
      //    data: JSON.stringify(setting)
      //  }, function (err, res, body) {
      //    if (err) reject(err);
      //    else fulfil(body);
      //  });
      //});

      return client.indices.putSettings({
        index: __index,
        //"settings": {
        similarity: {
          "custom_bm25": {
            "type": "BM25",
            "k1": "2.0",
            "b": "1.5"
          }
        }
        //}
      })
    }, function (err) {
      console.log('close index err ', err);
    })

    .then(function fulfil(body) {
      console.log('put setting ok ', body);
      return new Promise(function (fulfil, reject) {
        request({
          url: openUrl,
          method: 'POST'
        }, function (err, res, body) {
          if (err) reject(err);
          else fulfil(body);
        });
      })
    }, function reject(err) {
      console.log('put setting error', err);
    })

    .then(function fulfil(body) {
      console.log('open index succ ', body);
    }, function reject(err) {
      console.log('open index err', err);
    });

};

EsClient.prototype.afterIndexReady = function () {
  console.log('--------------afterIndexReady');
  this.indexReady = true;
  for (let type in this.enroll) {
    let mapping = this.enroll[type];
    this.createMappingIfNecessary(type, mapping);
  }
  this.emit('indexReady');
};

EsClient.prototype.afterTypeReady = function (type) {
  console.log('--------------afterTypeReady:type=' + type);
  this.emit('typeReady', type);
  // add to ready
  if (this.readyType.indexOf(type) < 0)
    this.readyType.push(type);

  // remove from register
  delete this.enroll[type];
};

EsClient.prototype.createMappingIfNecessary = function (type, mapping) {
  console.log('--------------createMappingIfNecessary');

  // check index ready or not
  if (!this.indexReady) {
    console.log('--------------createMappingIfNecessary: wait index ready ');
    this.registerType(type, mapping);
    return;
  }

  // check type ready or not
  if (this.readyType.indexOf(type) > 0) {
    console.log('--------------createMappingIfNecessary: this type already ready');
    self.afterTypeReady(type);
    return;
  }

  let self = this;
  client.indices.getMapping({
    index: __index,
    type: type
  }).then(function fullfil(body) { // create index if necessary
    let index = body[__index];
    let exists = !!index && !!index.mappings && !!index.mappings[type];

    if (!exists) {
      self.createMapping(type, mapping);
    } else {
      console.log('--------------createMappingIfNecessary: this type exist ready');
      self.afterTypeReady(type);
    }
  }, function reject(error) {
    console.error(error);
  });
};

EsClient.prototype.createMapping = function (type, mapping) {
  console.log('--------------createMapping:type=' + type);
  let self = this;
  client.indices.putMapping({
    index: __index,
    type: type,
    ignoreConflicts: true,
    body: mapping
  }).then(function (body) {
    self.afterTypeReady(type);
    return body;
  }, function reject(error) {
    console.error(error);
    self.afterTypeReady(type);
  });
};

EsClient.prototype.registerType = function (type, mapping) {
  console.log('--------------registerType:type=' + type);
  if (this.readyType.indexOf(type) >= 0) {
    console.log('--------------already ready state ,type=' + type);
    return;
  }

  console.log(this.enroll);
  if (this.enroll[type]) {
    console.log('--------------already registered ,type=' + type);
    return;
  }
  this.enroll[type] = mapping;
};


// init index
let esClient = new EsClient();
module.exports = esClient;
